﻿/*
 * Cratos Server - WCF Service
 * Cratos WCF Service class
 * 
 * Copyright (c) 2011-2012 Draco
 * 
 * This file is part of Cratos.
 * 
 * Cratos is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 2 of the License, or
 * (at your option) any later version.
 *
 * Cratos is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Cratos.  If not, see <http://www.gnu.org/licenses/>.
 * 
 *       .==.        .==.
 *      //'^\\      //^'\\
 *     // ^ ^\(\__/)/^ ^^\\
 *    //^ ^^ ^/6  6\ ^^ ^ \\
 *   //^ ^^ ^/( .. )\^ ^ ^ \\
 *  // ^^ ^/\| V""V|/\^ ^ ^ \\
 * //^^/\/ /  '~~'  \ \/\^ ^ \\
 *  ------------------------------------
 *  HERE BE DRAGONS
 *  
 *  Johan Dercksen - johan@draco.co.za
 */
using System;
using System.IO;
using System.Linq;
using System.Text;
using System.Reflection;
using System.ServiceModel;
using System.Configuration;
using System.IO.Compression;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Collections.ObjectModel;
using System.Runtime.Serialization.Formatters.Binary;

using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.Configuration;

using Cratos.Tools.General;
using Cratos.Tools.DataObject;

namespace Cratos.Server.WcfService
{
	/// <summary>
	/// Cratos WCF Service class
	/// </summary>
	public sealed class CratosWcfService : ICratosWcfService
	{
		#region Class Constant declarations
		#endregion

		#region Class Event declarations
		/// <summary>
		/// Log Message event
		/// </summary>
		public static event CratosLogMessageEventHandler LogMessage;
		#endregion

		#region Class Variable declarations
		#endregion

		#region Class Constructors & Destructors
		/// <summary>
		/// Class Static Constructor
		/// </summary>
		static CratosWcfService()
		{
			// Declare the required variables
			UnityConfigurationSection configSection;

			// Create and Setup the Unity Container
			CratosWcfService.Container = new UnityContainer();

			// Setup the relevant Configuration
			configSection = (UnityConfigurationSection)ConfigurationManager.GetSection("unity");
			if (configSection != null) { configSection.Configure(CratosWcfService.Container); }

			// Update the DO Container
			DOCratosGlobal.UnityContainer = CratosWcfService.Container;
		}
		#endregion

		#region Class Properties
		/// <summary>
		/// Unity Container
		/// </summary>
		public static IUnityContainer Container { get; set; }
		#endregion

		#region Class Methods
		/// <summary>
		/// Ping the Cratos WCF Service
		/// </summary>
		/// <returns></returns>
		public bool Ping() 
		{
			// Log the relevant message
			CratosWcfService.OnLogMessage(CratosLogSeverity.Informational, "Cratos WCF Service", "Ping()");

			// Return to calling method
			return (true);
		}

		/// <summary>
		/// Database Available request
		/// </summary>
		/// <param name="connectionName">Connection Name</param>
		/// <param name="dbActionType">Action Type</param>
		/// <returns>
		/// Returns true to indicate success
		/// </returns>
		public bool DatabaseAvailable(string connectionName, DOCratosActionType dbActionType)
		{
			// Declare the required variables
			bool							returnValue = false;
			ICratosDataAccess	dbAccess;

			// Log the relevant message
			CratosWcfService.OnLogMessage(CratosLogSeverity.Informational, "Cratos WCF Service", "DatabaseAvailable");

			try
			{
				// Make sure the DB Connection Name is valid
				if (ConfigurationManager.ConnectionStrings[connectionName] != null)
				{
					// Make sure we don't have a connection already
					switch (dbActionType)
					{
						case DOCratosActionType.StoredProcedure:
							// Create the relevant type
							dbAccess = CratosWcfService.Container.Resolve<ICratosDataAccess>("CratosDBStoredProcedure");
							break;

						case DOCratosActionType.SqlString:
							// Create the relevant type
							dbAccess = CratosWcfService.Container.Resolve<ICratosDataAccess>("CratosDBSqlString");
							break;

						default:
							throw new Exception("Invalid Database Command Type specified");
					}

					// Make sure we have a Data Access object
					if (dbAccess != null)
					{
						// Setup the connection
						dbAccess.ConnectionString = ConfigurationManager.ConnectionStrings[connectionName].ToString();

						// Connect and Disconnect from the database
						dbAccess.Connect();
						dbAccess.Disconnect();

						// Set the successful return value
						returnValue = true;
					}
				}
			}
			catch (Exception) { }

			// Return to calling method
			return (returnValue);
		}

		/// <summary>
		/// Get the server Date and Time
		/// </summary>
		/// <returns>Server Date and Time</returns>
		public DateTime GetServerDateTime()
		{
			// Declare the required variables

			// Log the relevant message
			CratosWcfService.OnLogMessage(CratosLogSeverity.Informational, "Cratos WCF Service", "GetServerDateTime");

			// Return to calling method
			return (DateTime.Now);
		}

		/// <summary>
		/// Cratos Log Message Event handler
		/// </summary>
		/// <param name="msgSeverity">Message Severity</param>
		/// <param name="msgSource">Message Source</param>
		/// <param name="newMessage">Message</param>
		public static void OnLogMessage(CratosLogSeverity msgSeverity, string msgSource, string newMessage)
		{
			// Declare the required variables

			// Raise the relevant event if required
			if (CratosWcfService.LogMessage != null) { CratosWcfService.LogMessage(msgSeverity, msgSource, newMessage); }

			// Return to calling method
			return;
		}

		/// <summary>
		/// Cratos Main generic web method
		/// </summary>
		/// <param name="ctCratos">Contract containing the required request information</param>
		/// <returns>Contract containing the relevant response information</returns>
		public CratosWcfContract Cratos(CratosWcfContract ctCratos)
		{
			// Declare the required variables

			// Create and Setup the relevant values
			ctCratos.Errors    = new Collection<string>();
			ctCratos.IsSuccess = false;

			// Perform the relevant Cartos action
			if (ctCratos.IsCollection) { return (this.CratosCollection(ctCratos)); }
			else { return(this.CratosSingle(ctCratos)); }
		}
		#endregion

		#region Class Support Methods
		private CratosWcfContract CratosCollection(CratosWcfContract ctCratos)
		{
			// Declare the required variables
			long		doCount = 0;
			object	ealCratos = null;

			try
			{
				// Log the relevant message
				CratosWcfService.OnLogMessage(CratosLogSeverity.Informational, "Cratos WCF Service", "CratosCollection()");

				// Deserialize the relevant object
				ealCratos = Activator.CreateInstance(Type.GetType(ctCratos.DODataType));
				ealCratos = DOCratosHelper.InvokeMethod(ealCratos, "CreateObjectFromStream", new object[] { ctCratos.DOStream });
				
				//ealCratos = DOCratosGlobal.CreateObjectFromStream<IDOCratosCollection<IDOCratos>>(ctCratos.DOStream);

				// Log the relevant message
				CratosWcfService.OnLogMessage(CratosLogSeverity.Informational, "Cratos WCF Service", 
																			string.Format("Processing {0} Action: {1}", ealCratos.GetType().Name, 
																																									DOCratosHelper.GetPropertyValue(ealCratos, "CollectionAction")));

				// Perform the correct action
				if ((bool) DOCratosHelper.InvokeMethod(ealCratos, "PerformAction", null))
				{
					// Retrieve the relevant values
					doCount = (int)DOCratosHelper.GetPropertyValue(ealCratos, "Count");

					// Update the Contract Success indicator
					ctCratos.IsSuccess = true;
				}
				else
				{
					// Process all errors
					foreach (CratosError currentError in ((IEnumerable<CratosError>) DOCratosHelper.GetPropertyValue(ealCratos, "Errors")))
					{
						if (currentError.RuntimeException == null)
						{
							ctCratos.Errors.Add(string.Format("[{0} - {1}] [{2}] {3}", currentError.Date.ToString("dd/MM/yyyy HH:mm:ss"),
																																				 currentError.Source,
																																				 currentError.Severity.ToString(),
																																				 currentError.Message));
						}
						else
						{
							ctCratos.Errors.Add(string.Format("[{0} - {1}] [{2}] {3}\n{4}", currentError.Date.ToString("dd/MM/yyyy HH:mm:ss"),
																																							currentError.Source,
																																							currentError.Severity.ToString(),
																																							currentError.Message,
																																							currentError.RuntimeException.ToString()));
						}
					}
				}

				// Serialize the object back
				ctCratos.DOStream = DOCratosGlobal.BinSerialize(ealCratos);
			}
			catch (Exception runtimeException)
			{
				// Add the error to the Error collection
				ctCratos.Errors.Add(string.Format("[{0} - {1}] [{2}] {3}\n{4}", DateTime.Now.ToString("dd/MM/yyyy HH:mm:ss"),
																																				"CratosWcfService.CratosCollection",
																																				CratosLogSeverity.Exception,
																																				runtimeException.Message,
																																				runtimeException.ToString()));
			}
			finally
			{
			}

			// Log the relevant message
			CratosWcfService.OnLogMessage(CratosLogSeverity.Informational, "Cratos WCF Service",
																		string.Format("CratosCollection() Completed [Return Value: {0} Count:{1} Size: {2}]", 
																									ctCratos.IsSuccess.ToString(), doCount, ctCratos.DOStream.LongLength));

			// Return to calling method
			return (ctCratos);
		}

		private CratosWcfContract CratosSingle(CratosWcfContract ctCratos)
		{
			// Declare the required variables
			IDOCratos doCratos;

			try
			{
				// Log the relevant message
				CratosWcfService.OnLogMessage(CratosLogSeverity.Informational, "Cratos WCF Service", "CratosSingle()");

				// Deserialize the Data Object
				doCratos = (IDOCratos) Activator.CreateInstance(Type.GetType(ctCratos.DODataType));
				doCratos = doCratos.CreateObjectFromStream(ctCratos.DOStream);

				// Perform the correct action
				if (doCratos.PerformAction())
				{
					// Serialize the object back
					ctCratos.DOStream  = doCratos.CreateSerializeStream();
					ctCratos.IsSuccess = true;
				}
				else
				{
					// Process all errors
					foreach (CratosError currentError in doCratos.Errors)
					{
						if (currentError.RuntimeException == null)
						{
							ctCratos.Errors.Add(string.Format("[{0} - {1}] [{2}] {3}", currentError.Date.ToString("dd/MM/yyyy HH:mm:ss"),
																																				 currentError.Source,
																																				 currentError.Severity.ToString(),
																																				 currentError.Message));
						}
						else
						{
							ctCratos.Errors.Add(string.Format("[{0} - {1}] [{2}] {3}\n{4}", currentError.Date.ToString("dd/MM/yyyy HH:mm:ss"),
																																							currentError.Source,
																																							currentError.Severity.ToString(),
																																							currentError.Message,
																																							currentError.RuntimeException.ToString()));
						}
					}
				}
			}
			catch (Exception runtimeException)
			{
				// Add the error to the Error collection
				ctCratos.Errors.Add(string.Format("[{0} - {1}] [{2}] {3}\n{4}", DateTime.Now.ToString("dd/MM/yyyy HH:mm:ss"),
																																				"CratosWcfService.CratosCollection",
																																				CratosLogSeverity.Exception,
																																				runtimeException.Message,
																																				runtimeException.ToString()));
			}
			finally
			{
			}

			// Log the relevant message
			CratosWcfService.OnLogMessage(CratosLogSeverity.Informational, "Cratos WCF Service",
																		string.Format("CratosSingle() Completed [Return Value: {0}]", ctCratos.IsSuccess.ToString()));

			// Return to calling method
			return (ctCratos);
		}
		#endregion
	}
}
